package net.xinshi.commons;

import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.concurrent.TimeUnit;

public class HttpClientManager {
    long socket_timeout;  //socket timeout
    long connection_timeout;
    boolean staleCheck;

    int max_total_connections;
    int max_connections_per_route;

    HttpClient httpClient = null;
    IdleConnectionMonitorThread checkerThread;


    public int getMax_total_connections() {
        return max_total_connections;
    }

    public void setMax_total_connections(int max_total_connections) {
        this.max_total_connections = max_total_connections;
    }

    public int getMax_connections_per_route() {
        return max_connections_per_route;
    }

    public void setMax_connections_per_route(int max_connections_per_route) {
        this.max_connections_per_route = max_connections_per_route;
    }

    public long getSocket_timeout() {
        return socket_timeout;
    }

    public void setSocket_timeout(long socket_timeout) {
        this.socket_timeout = socket_timeout;
    }

    public long getConnection_timeout() {
        return connection_timeout;
    }

    public void setConnection_timeout(long connection_timeout) {
        this.connection_timeout = connection_timeout;
    }

    public boolean isStaleCheck() {
        return staleCheck;
    }

    public void setStaleCheck(boolean staleCheck) {
        this.staleCheck = staleCheck;
    }

    ClientConnectionManager cm = null;

    void init() throws NoSuchAlgorithmException, KeyManagementException {
        if (httpClient != null) return;
        HttpParams params = new BasicHttpParams();
        ConnManagerParams.setMaxTotalConnections(params, max_total_connections);
        ConnManagerParams.setMaxConnectionsPerRoute(params, new ConnPerRouteBean(max_connections_per_route));
        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);

        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(
                new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
            }
        };
        ctx.init(null, new TrustManager[]{tm}, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        schemeRegistry.register(new Scheme("https", 443, ssf));

        cm = new ThreadSafeClientConnManager(params, schemeRegistry);
        httpClient = new DefaultHttpClient(cm, params);
        httpClient.getParams().setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, (int) connection_timeout);
        httpClient.getParams().setParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, staleCheck);
        httpClient.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, (int) socket_timeout);
        checkerThread = new IdleConnectionMonitorThread(cm);
        checkerThread.start();
    }

    public HttpClient getHttpClient() {
        if (httpClient == null) {
            try {
                init();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
        return httpClient;
    }

    public void shutdown() {
        checkerThread.shutdown();
        httpClient.getConnectionManager().shutdown();
    }
}

class IdleConnectionMonitorThread extends Thread {
    private final ClientConnectionManager connMgr;
    private volatile boolean shutdown;

    public IdleConnectionMonitorThread(ClientConnectionManager connMgr) {
        super();
        this.connMgr = connMgr;
    }

    @Override
    public void run() {
        try {
            while (!shutdown) {
                synchronized (this) {
                    wait(5000);
                    // Close expired connections
                    connMgr.closeExpiredConnections();
                    // Optionally, close connections
                    // that have been idle longer than 30 sec
                    connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
                }
            }
        } catch (InterruptedException ex) {
            // terminate
        }
    }

    public void shutdown() {
        shutdown = true;
        synchronized (this) {
            notifyAll();
        }
    }

}
